﻿using System;
using System.Collections.Generic;

namespace AutomaticScrewLocking.domain.modbus.protocol.service
{
    /// <summary>
    /// Modbus 抽象类
    /// </summary>
    public abstract class Modbus
    {
        // 对象锁
        public readonly object thisLock = new object();

        // 读取线圈状态（从站地址、开始地址值、操作的线圈数量、结果、错误信息、超时时间、重试次数）
        public abstract byte[] ReadCoilOuput(byte slaveId, ushort startAddress, ushort coilNums, out bool result, out string errorMsg, int timeout = 100, int readCount = 1);
        // 读取线圈数输入
        public abstract byte[] ReadCoilInput(byte slaveId, ushort startAddress, ushort coilNums, out bool result, out string errorMsg, int timeout = 100, int readCount = 1);
        // 写单个线圈（从站地址、开始地址值、状态、结果、错误信息、超时时间、重试次数）
        public abstract bool WriteSingleCoil(byte slaveId, ushort registerAddress, bool isOn, out string errorMsg, int timeout = 100, int writeCount = 1);
        // 写多个线圈
        public abstract bool WriteMultipleCoil(byte slaveId, ushort registerAddress, byte[] coilStatusVal, out string errorMsg, int timeout = 100, int writeCount = 1);
        // 写多个保持寄存器
        public abstract bool WriteMulitpleHoldRegister_Byte(byte slaveId, ushort registerAddress, byte[] registerValue, out string errorMsg, int timeout = 100, int writeCount = 1);
        // 写单个保持寄存器
        public abstract bool WriteSingleHoldRegister(byte slaveId, ushort registerAddress, ushort registerValue, out string errorMsg, int timeout = 100, int writeCount = 1);
        // 读多个保持寄存器（只读）
        public abstract byte[] ReadiInputRegister_Byte(byte slaveId, ushort registerAddress, ushort registerNums, out bool result, out string errorMsg, int timeout = 100, int readCount = 1);
        // 读多个保持寄存器（读写）
        public abstract byte[] ReadHoldRegister_Byte(byte slaveId, ushort registerAddress, ushort registerNums, out bool result, out string errorMsg, int timeout = 100, int readCount = 1);
        
        /// <summary>
        /// byte数组转换成short数组
        /// </summary>
        /// <param name="bytes">字节数组</param>
        /// <returns></returns>
        public ushort[] ConvertByteArrayToShortArray(byte[] bytes)
        {
            List<ushort> list = new List<ushort>();
            for(int i = 0; i < bytes.Length; i+=2)
            {
                // Modbus 协议中寄存器的字节序为 大端序（Big-Endian）（高位字节在前，低位字节在后）
                byte[] byteArr = new byte[2];
                byteArr[1] = bytes[i];
                byteArr[0] = bytes[i+1];
                ushort value = BitConverter.ToUInt16(byteArr, 0);
                list.Add(value);
            }
            return list.ToArray();
        }

        /// <summary>
        /// 将字节数组转换为十六进制字符串
        /// </summary>
        /// <param name="byteArray">字节数组</param>
        /// <param name="result">结果</param>
        /// <param name="errorMsg">错误信息</param>
        /// <param name="splitStr">分隔符</param>
        /// <param name="isUpper">大写标志</param>
        /// <returns></returns>
        public string ConvertByteArrayToHexStringArray(byte[] byteArray, out bool result, out string errorMsg, string splitStr = " ", bool isUpper = true)
        {
            errorMsg = string.Empty;
            result = false;
            string value = string.Empty;
            List<string> list = new List<string>();
            try
            {
                foreach(byte b in byteArray)
                {
                    string empty = string.Empty;
                    // PadLeft(2, '0') 确保结果是两位数，确的补全0
                    empty = ((!isUpper) ? Convert.ToString(b, 16).PadLeft(2, '0') : Convert.ToString(b, 16).ToUpper().PadLeft(2, '0'));
                    list.Add(empty);
                }

                // 处理空字符串
                if (string.IsNullOrEmpty(splitStr))
                {
                    splitStr = "";
                }
                value = string.Join(splitStr, list);
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
            }

            return value;
        }

        /// <summary>
        /// 从字节中提取指定索引位置的位值
        /// </summary>
        /// <param name="data">字节数据</param>
        /// <param name="bitIndex">索引</param>
        /// <returns></returns>
        public byte GetValByBitIndex(byte data, int bitIndex, out string errorMsg)
        {
            byte result = 0;
            errorMsg = string.Empty;
            try
            {
                // 将目标位右移到最低位
                byte b = (byte)(data >> bitIndex);
                // 与1进行"与运算"，保留最低位，过滤其他位
                result = (byte)(b & 1u);
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
            }
            return result;
        }
    }
}
